<?php
/**
 * DynamicParent
 * 
 * @category	Util
 * @package		dayscript.util.dynamic
 * @subpackage	
 * @author		Nelson Daza <ndaza@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license		
 * @version		1.0
 * @version		$Revision: 0 $
 * @filesource	
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses		
 * @since		1.0
 * @modifiedby	$LastChangedBy: Nelson Daza $
 * @modified	$Date: 2009-08-13 $
 */

namespace dayscript\util\dynamic;

/**
 * DynamicParent
 * 
 * Abstract
 * 
 * @category	Util
 * @package		dayscript.util.dynamic
 * @subpackage	
 * @author		Nelson Daza <ndaza@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license		
 * @version		1.0
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses		dayscript\util\dynamic\Common, \dayscript\Map, \dayscript\exception\ErrorCallerException
 * @since		1.0
 * @modifiedby	$LastChangedBy: Nelson Daza $
 * @modified	$Date: 2009-08-13 $
 */
abstract class DynamicParent extends \dayscript\util\dynamic\Common	{
	private $children = null;
	private $childMethods = null;
	private $childProperties = null;
	/**
	 * Extends class by child class.
	 *
	 * @param mixed $object
	 * @return bool
	 */
	function extend( $object )	{
		if ( !\is_object( $object ) )	{
			if ( \is_string( $object ) )
				$object = new $object( );
		}

		$this->children = new \dayscript\Map( );
		$this->childMethods = new \dayscript\Map( );
		$this->childProperties = new \dayscript\Map( );
		$this->children[\get_class( $object )] = $object;

		$reflection = new \ReflectionClass( $object );
		foreach( $reflection->getMethods( ) as $method )	{
			if( !$method->isPrivate( ) && \strpos( $method->getName( ), '__') !== 0 )
				$this->childMethods[$method->getName( )] = array( $reflection->getName( ), ( $method->isPublic( ) ? 'public' : 'protected' ) );
		}
		foreach( $reflection->getProperties( ) as $property )	{
			if( !$property->isPrivate( ) )
				$this->childProperties[$property->getName( )] = array( $reflection->getName( ), ( $property->isPublic( ) ? 'public' : 'protected' ) );
		}
		if( $object instanceof \dayscript\util\dynamic\DynamicParent )
			$object->setParent( $this );
		return true;
	}
	/**
	 * Drops a child
	 *
	 * @param string $child
	 * @return bool
	 */
	function drop( $child )	{
		if( !$this->children[$child] )
			return null;

		foreach ( $this->childMethods as $method )	{
			if ( $method[0] == $child )
				unset( $this->childMethods[$method] );
		}
		foreach ( $this->childProperties as $property )	{
			if ( $property[0] == $child )
				unset( $this->childProperties[$property] );
		}
		return $this->children->remove( $child );
	}
	/**
	 * Magic method for calling the object's methods
	 * 
	 * @param string $method
	 * @param mixed $arguments
	 * @return mixed
	 */
	function __call( $method, $arguments )	{
		if ( $this->childMethods[$method] )	{
			$object = $this->children[$this->childMethods[$method][0]];
			if( $this->childMethods[$method][1] == 'public' )
				return \call_user_func_array( array( $object, $method ), $arguments );
			if( $object instanceof \dayscript\util\dynamic\DynamicParent )
				return $object->__callMethod( $method, $arguments );
			return false;
		}
		throw new \dayscript\exception\ErrorCallerException( 'Call to undefined Method "' . $method . '"', \E_USER_ERROR );
	}
	/**
	 * Magic method for getting the object's members values 
	 * 
	 * @param string $name
	 * @return mixed
	 */
	function __get( $name )	{
		if( \strpos( $name, '__' ) === 0 )	{
			$name = \substr( $name, 2);
			return $this->children[$name];
		}
		if( $this->childProperties[$name] )	{
			$property = $this->childProperties[$name];
			if( $property[1] == 'public' )
				return $this->children[$property[0]]->$name;
			if( $this->children[$property[0]] instanceof \dayscript\util\dynamic\DynamicParent )
				return $this->children[$property[0]]->__getProperty( $name );
		}
		throw new \dayscript\exception\ErrorCallerException( 'Call to undefined property "' . $name . '"', \E_USER_ERROR );
	}
	/**
	 * Magic method for setting the object's members values 
	 * 
	 * @param string $name
	 * @param mixed $value
	 */
	function __set( $name, $value )	{
		$property = $this->childProperties[$name];
		if( $property != null  )	{
			if( $property[1] == 'public' )
				$this->children[$property[0]]->$name = $value;
			else if($this->children[$property[0]] instanceof \dayscript\util\dynamic\DynamicParent )
				$this->children[$property[0]]->__setProperty($name, $value);
		}
		else
			throw new \dayscript\exception\ErrorCallerException( 'Call to undefined property "' . $name . '"', \E_USER_ERROR );
	}
	/**
	 * Methods verification
	 * 
	 * @param string $method
	 * @return boolean 
	 */
	function hasMethod( $method )	{
		return ( isset( $this->childMethods[$method] ) || \method_exists( $this, $method ) );
	}
	/**
	 * Properties verification
	 * 
	 * @param string $property
	 * @return boolean
	 */
	function hasProperty( $property )	{
		return ( isset( $this->childProperties[$property] ) || \property_exists( $this, $property ) );
	}
}